
#pragma once

#include <spdlog/spdlog.h>
#include <entt/entt.hpp>
#include "ly/array.hpp"
#include "ly/export.hpp"
#include "ly/manager.hpp"
#include "ly/properties.hpp"
#include "ly/trans.hpp"

namespace ly {

class Instances;
class Cell;

class LY_API CellInst {
 public:
  CellInst() {};
  CellInst(cell_id_type id) : m_cell_id(id) {};

  void cell_id(cell_id_type id) { m_cell_id = id; }
  cell_id_type cell_id() { return m_cell_id; }

 private:
  cell_id_type m_cell_id;
};

/**
 * @brief 引用管理
 *
 * 管理所有引用对象。
 *
 */
class LY_API Instances {
 public:
  typedef std::shared_ptr<Cell> cell_sptr;
  typedef std::weak_ptr<Cell> cell_wptr;
  typedef std::shared_ptr<Instances> instance_sptr;
  typedef entt::registry reg_t;

 public:
  Instances();
  Instances(cell_wptr parent_cell) : mp_cell(parent_cell) {};
  ~Instances();

  template <typename... Args>
  static instance_sptr create(Args&&... args) {
    return std::make_shared<Instances>(std::forward<Args>(args)...);
  }

  reg_t& reg() { return m_reg; }
  cell_sptr cell() { return mp_cell.lock(); };
  void cell(cell_sptr c) { mp_cell = c; };

  entt::entity create_entity() {
    auto e = m_reg.create();
    return e;
  };

  entt::entity create_inst(const CellInst& inst) {
    auto e = m_reg.create();
    emplace(e, inst);
    return e;
  };

  void emplace_or_replace(entt::entity e, const CellInst& com);
  void emplace_or_replace(entt::entity e, const Trans& com);
  void emplace_or_replace(entt::entity e, const CplxTrans& com);
  void emplace_or_replace(entt::entity e, const ObjectWithProperties& com);
  void emplace_or_replace(entt::entity e, const RegularArray& com);
  void emplace_or_replace(entt::entity e, const RectangularArray& com);

  void undo(Op* op);
  void redo(Op* op);

  bool is_editable();
  void print_all();

 private:
  template <typename T>
  void emplace(entt::entity e, const T& comp);

 private:
  cell_wptr mp_cell;
  reg_t m_reg;
};

}  // namespace ly
